﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace LightINI
{
    /// <summary>
    /// INI 文件读写工具
    /// </summary>
    public partial class Ini : IEnumerable<Ini.Section>
    {
        /// <summary>
        /// 注释字符
        /// </summary>
        private const string COMMENT_CHARS = ";#";

        /// <summary>
        /// 所有配置段
        /// </summary>
        private readonly List<Section> Sections;

        /// <summary>
        /// 打开的文件名，为null时表示不是从文件打开的
        /// </summary>
        private string filename;

        /// <summary>
        /// 配置段数量
        /// </summary>
        public int Count => Sections.Count;

        /// <summary>
        /// 更改前的事件
        /// </summary>
        public event PreEvent OnChanging;

        /// <summary>
        /// 更改后的事件
        /// </summary>
        public event PostEvent OnChanged;

        /// <summary>
        /// 触发数据项修改事件
        /// </summary>
        /// <param name="type"></param>
        /// <param name="target"></param>
        /// <param name="action"></param>
        private void EmitEvent(ChangeType type, object target, Action action)
        {
            var preArgs = new PreEventArgs(type, target);
            OnChanging.Invoke(this, preArgs);
            if (preArgs.Cancel)
            {
                return;
            }
            action.Invoke();
            var postArgs = new PostEventArgs(type, target);
            OnChanged.Invoke(this, postArgs);
        }

        public Ini()
        {
            Sections = new List<Section>();
        }

        /// <summary>
        /// 读取指定名称的配置段
        /// </summary>
        /// <param name="key"></param>
        /// <param name="append">在配置段不存在时，是否自动添加</param>
        /// <returns></returns>
        public Section Get(string sectionName, bool append = false)
        {
            var section = Sections.FirstOrDefault(s => s.Name.Equals(sectionName, StringComparison.OrdinalIgnoreCase));

            if (append)
            {
                section = Add(sectionName);
            }

            return section;
        }

        /// <summary>
        /// 设置指定配置段下的配置项值
        /// </summary>
        /// <param name="sectionName"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Set(string sectionName, string key, string value)
        {
            var section = Get(sectionName);
            if (section == null)
            {
                section = Add(sectionName);
            }
            // 添加操作可能被阻止
            if (section == null)
            {
                return;
            }
            EmitEvent(ChangeType.EditSection, section, () =>
            {
                section.Set(key, value);
            });
        }

        /// <summary>
        /// 添加指定名称的配置段
        /// </summary>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public Section Add(string sectionName)
        {
            Section section = null;
            EmitEvent(ChangeType.AddSection, section, () =>
            {
                section = new Section(this)
                {
                    Name = sectionName
                };
                Sections.Add(section);
            });
            return section;
        }

        /// <summary>
        /// 移除指定配置段
        /// </summary>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public Section Remove(string sectionName)
        {
            var found = Get(sectionName);
            if (found != null)
            {
                EmitEvent(ChangeType.RemoveSection, found, () =>
               {
                   Sections.Remove(found);
               });
            }
            return found;
        }

        /// <summary>
        /// 移除指定项
        /// </summary>
        /// <param name="sectionName"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public Section Remove(string sectionName, string key)
        {
            var found = Get(sectionName);
            if (found != null)
            {
                found.Remove(key);
            }
            return found;
        }

        /// <summary>
        /// 获取指定配置段下的配置项的值
        /// </summary>
        /// <param name="sectionName"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetValue(string sectionName, string key)
        {
            var section = Get(sectionName);
            return section?.GetValue(key);
        }

        /// <summary>
        /// 是否存在指定配置段
        /// </summary>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public bool Has(string sectionName)
        {
            return Sections.Any(s => s.Name.Equals(sectionName, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 是否存在指定配置段，并且配置段下存在指定的项
        /// </summary>
        /// <param name="sectionName"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Has(string sectionName, string key)
        {
            var section = Get(sectionName);
            return section != null && section.Has(key);
        }

        /// <summary>
        /// 将内容输出为INI文件结构
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Join("", Sections.Select(i => i.ToString()));
        }

        public static Ini Open(string filename)
        {
            var ini = Parser.Parse(File.ReadAllLines(filename));
            ini.filename = filename;
            return ini;
        }

        /// <summary>
        /// 从文本内容加载INI文件
        /// </summary>
        /// <param name="content">配置文件的内容</param>
        /// <returns></returns>
        public static Ini Load(string content)
        {
            return Parser.Parse(content.Split('\n'));
        }

        /// <summary>
        /// 保存到文件
        /// </summary>
        public void Save(string filename = null)
        {
            File.WriteAllText(filename ?? this.filename, ToString());
        }

        public IEnumerator<Section> GetEnumerator()
        {
            return Sections.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Sections.GetEnumerator();
        }
    }
}
